package com.ibeeking.gmc.global.g.rest.service.impl;

import com.ibeeking.gmc.global.g.rest.service.IDictTypeService;
import com.ibeeking.gmc.global.g.rest.service.IDictValueService;
import com.ibeeking.found.common.base.BaseDO;
import com.ibeeking.found.common.entity.DictDTO;
import com.ibeeking.found.common.enums.PublishStatusEnum;
import com.ibeeking.gmc.global.service.common.bo.DictTypeBO;
import com.ibeeking.gmc.global.service.common.constants.RedisKeyConstant;
import com.ibeeking.gmc.global.service.common.dos.DictValueDO;
import com.ibeeking.gmc.global.service.common.param.DictValueParam;
import com.ibeeking.gmc.global.service.common.query.DictValuePageQuery;
import com.ibeeking.gmc.global.service.common.vo.DictSelectVO;
import com.ibeeking.gmc.global.service.common.vo.DictValuePageVO;
import com.ibeeking.gmc.global.service.common.vo.DictValueVO;
import com.ibeeking.gmc.global.service.mapper.DictValueMapper;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.redis.annotation.CacheEvict;
import com.ibeeking.nematos.redis.annotation.CacheQuery;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName DictValueServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2020-12-02 16:06
 **/
@Service
public class DictValueServiceImpl extends ServiceImpl<DictValueMapper, DictValueDO> implements IDictValueService {

    @Resource
    private DictValueMapper dictValueMapper;
    @Resource
    private IDictTypeService dictTypeServiceImpl;
    @Resource
    private RedisUtils redisUtils;

    @Override
    public Page<DictValuePageVO> pageList(DictValuePageQuery dictValuePageQuery) {
        LambdaQueryWrapper<DictValueDO> queryWrapper = new LambdaQueryWrapper<>(DictValueDO.class);
        queryWrapper.eq(DictValueDO::getParentId, dictValuePageQuery.getParentId()).orderByAsc(DictValueDO::getSort).orderByDesc(BaseDO::getCreateTime);

        if (null != dictValuePageQuery.getPublishStatus()) {
            queryWrapper.eq(DictValueDO::getPublishStatus, dictValuePageQuery.getPublishStatus());
        }

        if (null != dictValuePageQuery.getIsDefault()) {
            queryWrapper.eq(DictValueDO::getIsDefault, dictValuePageQuery.getIsDefault());
        }

        if (StringUtils.isNotBlank(dictValuePageQuery.getLabel())) {
            queryWrapper.like(DictValueDO::getLabel, dictValuePageQuery.getLabel());
        }

        if (StringUtils.isNotBlank(dictValuePageQuery.getValue())) {
            queryWrapper.like(DictValueDO::getValue, dictValuePageQuery.getValue());
        }

        Page<DictValueDO> page = dictValueMapper.selectPage(new Page<>(dictValuePageQuery.getPageNum(), dictValuePageQuery.getPageSize()), queryWrapper);
        return BeanUtil.convertPage(page, DictValuePageVO.class);
    }

    @Transactional
    @Override
    public Boolean add(DictValueParam dictValueParam) {
        IDictValueService bean = SpringBeanUtil.getBean(IDictValueService.class);
        Long id1 = bean.checkValue(dictValueParam.getValue(), dictValueParam.getParentId());
        if (null != id1) {
            throw new BusinessException("存在同名字典值");
        }
        Long id2 = bean.checkLable(dictValueParam.getLabel(), dictValueParam.getParentId());
        if (null != id2) {
            throw new BusinessException("存在同标签字典值");
        }
        updateDefault(dictValueParam.getIsDefault(), dictValueParam.getParentId());

        DictValueDO dictValueDO = BeanUtil.convertBean(dictValueParam, DictValueDO.class);
        DictTypeBO dictTypeBO = dictTypeServiceImpl.queryById(dictValueParam.getParentId());
        dictValueDO.setTypeCode(dictTypeBO.getType());
        dictValueDO.setCreateTime(LocalDateTime.now());
        dictValueDO.setModifyTime(LocalDateTime.now());
        return dictValueMapper.insert(dictValueDO) > 0;
    }

    @Transactional
    @CacheEvict(cacheName = RedisKeyConstant.MODULE + RedisKeyConstant.GLOBAL_DICT_VALUE, key = "#dictValueParam.id")
    @Override
    public Boolean modify(DictValueParam dictValueParam) {
        IDictValueService bean = SpringBeanUtil.getBean(IDictValueService.class);
        DictValueDO view = dictValueMapper.selectById(dictValueParam.getId());
        Long id1 = bean.checkValue(dictValueParam.getValue(), view.getParentId());
        if (null != id1 && !id1.equals(dictValueParam.getId())) {
            throw new BusinessException("存在同名字典值");
        }
        Long id2 = bean.checkLable(dictValueParam.getLabel(), view.getParentId());
        if (null != id2 && !id2.equals(dictValueParam.getId())) {
            throw new BusinessException("存在同标签字典值");
        }
        updateDefault(dictValueParam.getIsDefault(), view.getParentId());
        DictValueDO dictValueDO = BeanUtil.convertBean(dictValueParam, DictValueDO.class);
        dictValueDO.setParentId(null);
        dictValueDO.setTypeCode(null);
        dictValueDO.setModifyTime(LocalDateTime.now());
        return dictValueMapper.updateById(dictValueDO) > 0;
    }

    @CacheQuery(cacheName = RedisKeyConstant.MODULE + RedisKeyConstant.GLOBAL_DICT_VALUE, key = "#id", expires = RedisKeyConstant.MONTH)
    @Override
    public DictValueVO view(Long id) {
        DictValueDO dictValueDO = dictValueMapper.selectById(id);
        return BeanUtil.convertBean(dictValueDO, DictValueVO.class);
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE + RedisKeyConstant.GLOBAL_DICT_VALUE, key = "#id")
    @Override
    public Boolean changeStatus(Long id, Integer status, Long userId) {
        DictValueDO dictValueDO = new DictValueDO();
        dictValueDO.setId(id);
        dictValueDO.setPublishStatus(status);
        dictValueDO.setModifyBy(userId);
        dictValueDO.setModifyTime(LocalDateTime.now());
        return dictValueMapper.updateById(dictValueDO) > 0;
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE + RedisKeyConstant.GLOBAL_DICT_VALUE, key = "#id")
    @Override
    public Boolean del(Long id) {
        return dictValueMapper.deleteById(id) > 0;
    }

    @Override
    public Long checkValue(String value, Long parentId) {
        DictValueDO dictValueDO = dictValueMapper.selectOne(new LambdaQueryWrapper<>(DictValueDO.class).eq(DictValueDO::getValue, value).eq(DictValueDO::getParentId, parentId));
        return null != dictValueDO ? dictValueDO.getId() : null;
    }

    @Override
    public Long checkLable(String lable, Long parentId) {
        DictValueDO dictValueDO = dictValueMapper.selectOne(new LambdaQueryWrapper<>(DictValueDO.class).eq(DictValueDO::getLabel, lable).eq(DictValueDO::getParentId, parentId));
        return null != dictValueDO ? dictValueDO.getId() : null;
    }

    @Override
    public List<DictDTO> queryDictByTypes(String[] dictTypes) {
        List<DictDTO> result = new ArrayList<>();
        IDictValueService bean = SpringBeanUtil.getBean(IDictValueService.class);
        Arrays.stream(dictTypes).forEach(dictType -> {
            Optional.ofNullable(bean.queryDictByType(dictType)).ifPresent(result::addAll);
        });
        return result;
    }

    @Override
    public List<DictDTO> queryDictByType(String dictType) {
        Map<String, Object> dictMap = redisUtils.hGet(RedisKeyConstant.MODULE + RedisKeyConstant.GLOBAL_DICT_VALUE + dictType);
        if (!CollectionUtils.isEmpty(dictMap)) {
            return dictMap.values().parallelStream().map(o -> BeanUtil.convertBean(o, DictDTO.class)).collect(Collectors.toList());
        } else {
            List<DictValueDO> dictValues = dictValueMapper.selectList(new LambdaQueryWrapper<>(DictValueDO.class)
                    .eq(DictValueDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                    .eq(DictValueDO::getTypeCode, dictType)
                    .orderByDesc(DictValueDO::getIsDefault)
                    .orderByAsc(DictValueDO::getSort)
                    .orderByDesc(DictValueDO::getCreateTime)
            );
            if (Collections3.isEmpty(dictValues)) {
                return Collections3.emptyList();
            }
            List<DictDTO> dicts = JsonUtils.jsonToList(dictValues, DictDTO.class);
            redisUtils.hSet(RedisKeyConstant.MODULE + RedisKeyConstant.GLOBAL_DICT_VALUE + dictType, dicts.parallelStream().collect(Collectors.toMap(DictDTO::getValue, dictDTO -> dictDTO, (s, a) -> a)), RedisKeyConstant.HALF_DAY);
            return dicts;
        }
    }

    @Override
    public List<DictDTO> queryDictLables(Map<String, String> paramMap) {
        List<DictDTO> result = new ArrayList<>();
        IDictValueService bean = SpringBeanUtil.getBean(IDictValueService.class);
        for (Map.Entry<String, String> entry : paramMap.entrySet()) {
            Optional.ofNullable(bean.queryDictLable(entry.getKey(), entry.getValue())).ifPresent(result::add);
        }
        return result;
    }

    @Override
    public DictDTO queryDictLable(String dictType, String dictValue) {
        IDictValueService bean = SpringBeanUtil.getBean(IDictValueService.class);
        List<DictDTO> dicts = bean.queryDictByType(dictType);
        if (Collections3.isEmpty(dicts)) {
            return null;
        }
        return dicts.parallelStream().filter(dictDTO -> dictValue.equals(dictDTO.getValue())).findAny().get();
    }

    @Override
    public List<DictSelectVO> queryListByType(String dictType) {
        List<DictValueDO> dictValueDOS = dictValueMapper.selectList(new LambdaQueryWrapper<>(DictValueDO.class)
                .eq(DictValueDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                .eq(DictValueDO::getTypeCode, dictType)
                .orderByDesc(DictValueDO::getIsDefault)
                .orderByAsc(DictValueDO::getSort)
                .orderByDesc(DictValueDO::getCreateTime)
        );
        if (Collections3.isEmpty(dictValueDOS)) {
            return Collections3.emptyList();
        }
        return JsonUtils.jsonToList(dictValueDOS, DictSelectVO.class);
    }

    private void updateDefault(Integer isDefault, Long parentId) {
        if (1 == isDefault) {
            DictValueDO update = new DictValueDO();
            update.setIsDefault(0);
            update.setModifyTime(LocalDateTime.now());
            dictValueMapper.update(update, new LambdaUpdateWrapper<>(DictValueDO.class).eq(DictValueDO::getParentId, parentId));
        }
    }

}